/**
	License:		Artistic license 2.0 (see license.txt, also available online at http://www.perlfoundation.org/artistic_license_2_0)
	Authors:		OrbitalLab (http://www.orbitallab.ru/moonglide/), 2007-2009

	File:			OpenGLOffscreen.d
	Description:	offscreen render manager
	Date:			08.02.2008 by Jurec
*/
module moonglide.renderer.opengl.OpenGLOffscreen;

import
	moonglide.renderer.IRendererOffscreen,

	moonglide.renderer.opengl.GL,
	moonglide.renderer.opengl.OpenGLRenderer,
	moonglide.util.MoonUtil;

debug import
	moonglide.io.LogSystem,
    moonglide.math.MoonMath;

class COpenGLFBO_depthRB
{
    protected:
        uint _depth_rb = 0;
        bool _owner = false;
        uint _wid = 64, _hei = 64;
    public:
        void fromExist(COpenGLFBO_depthRB someRB)
        {
            destroy();
            _depth_rb = someRB.getHandle();
            _owner = false;
        }

        void create(uint wid = 64, uint hei = 64)
        {
            destroy();
            _wid = wid;
            _hei = hei;
            glGenRenderbuffersEXT(1, &_depth_rb);
            glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _depth_rb);
            glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,GL_DEPTH_COMPONENT24,_wid,_hei);
            _owner = true;
        }

        void destroy()
        {
            if (_owner)
                glDeleteRenderbuffersEXT(1,&_depth_rb);
        }

        void resize(uint wid, uint hei)
        {
            if (_owner)
            {
                _wid = wid;
                _hei = hei;
                glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _depth_rb);
                glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, _wid, _hei);
            }
        }

        uint getHandle()
        {
            return _depth_rb;
        }
}

class COpenGLFBO
{
    protected:
        uint _frameBuff = 0;
        uint _wid = 0,
             _hei = 0;
        COpenGLFBO_depthRB _depthRB;
        bool _init = false,
             _depth = true,
             _depth_create = true;
        this() {}

        bool fboIsOk ()
        {
            uint	complete = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);

            if (complete == GL_FRAMEBUFFER_COMPLETE_EXT)
                return true;

            switch (complete)
            {
                case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
                moonAssert(0,"FBO has one or several image attachments with different internal formats",__FILE__,__LINE__);
                break;
                case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
                moonAssert(0,"FBO has one or several image attachments with different dimensions",__FILE__,__LINE__);
                break;
             //   case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
             //   moonAssert(0,"FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT",__FILE__,__LINE__);
             //   break;
                case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
                moonAssert(0,"FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT",__FILE__,__LINE__);
                break;
                case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
                moonAssert(0,"FBO format unsupported",__FILE__,__LINE__);
                break;
                case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT :
                moonAssert(0,"FRAMEBUFFER_INCOMPLETE_ATTACHMENT",__FILE__,__LINE__);
                break;
                case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT :
                moonAssert(0,"FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER",__FILE__,__LINE__);
                break;
                case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT :
                moonAssert(0,"FRAMEBUFFER_INCOMPLETE_READ_BUFFER",__FILE__,__LINE__);
                break;
            }

            moonAssert(0,"Unknown FBO error",__FILE__,__LINE__);

            return false;
        }
    public:
        this( uint wid, uint hei, bool depth = true )
        {
            debug attentionLog( "CBuf : created" );
            _wid = wid;
            _hei = hei;
            _depth_create = depth;
            _depthRB = new COpenGLFBO_depthRB();
        }

        ~this()
        {
            deInit();
            delete _depthRB;
        }

        COpenGLFBO_depthRB getDepthRenderBuffer()
        {
            return (_depth) ? _depthRB : null;
        }

        void init()
        {
            debug attentionLog( "CBuf : init" );
            glGenFramebuffersEXT(1, &_frameBuff);

            _init = true;

            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _frameBuff);

            if( _depth_create )
            {
                _depthRB.create(_wid,_hei);
                glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,GL_RENDERBUFFER_EXT, _depthRB.getHandle);
            }
            /*if( _depth_create )
            {
                _depth = true;
                glGenRenderbuffersEXT(1, &_depth_rb);
                glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _depth_rb);
                glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,GL_DEPTH_COMPONENT24,_wid,_hei);

                glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,GL_RENDERBUFFER_EXT, _depth_rb);
            }*/

           debug attentionLog( "CBuf : frameBuff = "~intToStr(_frameBuff)~" depth_rb = "~intToStr(_depthRB.getHandle));
            resize(_wid, _hei);
        }

        bool isInited() { return _init; }

        bool deInit()
        {
            if( !_init )
                return true;
            debug attentionLog( "CBuf : frameBuff = "~intToStr(_frameBuff)~" depth_rb = "~intToStr(_depthRB.getHandle()));
            debug attentionLog( "CBuf : deInit" );
            glDeleteFramebuffersEXT(1,&_frameBuff);

            if( _depth_create )
                _depthRB.destroy;

            return fboIsOk;
        }

        void attachDepth(COpenGLFBO_depthRB fboRB)
        in
        {
            moonAssert( fboRB !is null, "CBuf: fboRB is null", __FILE__,__LINE__ );
        }
        body
        {
            uint depthFboObj = fboRB.getHandle();

//            _depthRB.destroy;

            _depth = true;
            _depthRB.fromExist(fboRB);
/*
            glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _depth_rb);
            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _frameBuff);
            glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, _depth_rb);
            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
  */          //debug attentionLog( "CBuf : attachDepthFrom "~intToStr(depthFboObj)~"");
        }

        void resize( uint wid, uint hei )
        in
        {
            moonAssert( _frameBuff != 0, "CBuf !inited", __FILE__,__LINE__ );
        }
        body
        {
        //    debug attentionLog( "CBuf : frameBuff = "~intToStr(_frameBuff)~" depth_rb = "~intToStr(_depth_rb));
          //  debug attentionLog( "CBuf : resized from " ~ intToStr(_wid) ~ " x " ~ intToStr(_hei) ~
            //                                    " to " ~ intToStr(wid) ~ " x " ~ intToStr(hei) );

            //if( wid == _wid && hei == _hei )
            //    return;

            GetGLError(__FILE__,__LINE__);
            _wid = wid;
            _hei = hei;
            GetGLError(__FILE__,__LINE__);
            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _frameBuff);
            GetGLError(__FILE__,__LINE__);
            _depthRB.resize(_wid,_hei);
            /*
            if( _depth_create ) // ( _depth ) ?
            {
                glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _depth_rb);
                glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,GL_DEPTH_COMPONENT24,_wid,_hei);
            }
            //glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,_wid,_hei);
*/
            GetGLError(__FILE__,__LINE__);
            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
        }

        void bind()
        {
            glViewport(0, 0, _wid,_hei);
            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _frameBuff);
            if( _depth )
                glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _depthRB.getHandle());
        }

        void unBind()
        {
            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
        }

        void bindAndAttachTexture2d(CResTexture tex, int no = 0)
        in
        {
            moonAssert( tex !is null, "tex is null", __FILE__, __LINE__ );
            moonAssert( tex.data.length>=0,"tex is empty",__FILE__,__LINE__);
        }
        body
        {
            if(! ((tex.data[0].width == _wid) && (tex.data[0].height == _hei)) )
            {
                debug
                {
                    attentionLogR( tex.id );
                    attentionLogR( intToStr(_wid) ~ "x" ~ intToStr(_hei) );
                    attentionLogR( intToStr(tex.data[0].width) ~ "x" ~ intToStr(tex.data[0].height) );
                }
                moonAssert(false, "texture has different dimensions!",__FILE__,__LINE__);
            }
            if( !tex.loaded )
            moonAssert(false, "texture isn`t loaded",__FILE__,__LINE__);
            uint handl =  tex.textureID;

 //           debug attentionLog( "CBuf : frameBuff = "~intToStr(_frameBuff)~" depth_rb = "~intToStr(_depth_rb));
   //         debug attentionLog( "CBuf : attachedTexture2d " ~ tex.id ~ " res: " ~ intToStr(tex.data[0].width) ~ "x" ~ intToStr(tex.data[0].height));

            bind;
GetGLError(__FILE__,__LINE__);
            if( tex.data[0].format == IL_MOON_DEPTH24 )
            {
               // moonAssert(!_depth, "CBuf: try to bind depth texture to buffer with depth renderbuffer",__FILE__,__LINE__);
       //         debug attentionLog( "CBuf : attachedTexture to GL_DEPTH_ATTACHMENT_EXT");
                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,GL_TEXTURE_2D,handl,0);
              //  glDrawBuffer(GL_NONE);
                //glReadBuffer(GL_NONE);
            } else
            {
                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+no,GL_TEXTURE_2D,handl,0);
     //           debug attentionLog( "CBuf : attachedTexture to GL_COLOR_ATTACHMENT"~intToStr(no)~"_EXT");

            }
            fboIsOk;
GetGLError(__FILE__,__LINE__);

        }

}


